package view;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.util.ArrayList;

import javax.swing.JComponent;

import controller.MetaDeckController;

import model.Card;
import model.CardStack;
import model.TableModel;

@SuppressWarnings("serial")
public class TableComponent extends JComponent {
	private TableModel model;
	private Point selectionBoxOrigin;
	private TableSelection selection;
	private boolean dragging;
	private static final int AFFORDANCE_BUBBLE_RADIUS = 20;
	public TableComponent(TableModel model) {
		this.model = model;
	}
	public Dimension getPreferredSize() {
		return new Dimension(350, 350);
	}
	
	public Point getSelectionBoxOrigin() {
		return selectionBoxOrigin;
	}
	public TableSelection getSelection() {
		return selection;
	}
	public boolean getDragging() {
		return dragging;
	}
	public void setSelectionBoxOrigin(Point p) {
		selectionBoxOrigin = p;
		repaint();
	}
	public void setSelection(TableSelection s) {
		selection = s;
		repaint();
	}
	public void setDragging(boolean b) {
		dragging = b;
		repaint();
	}
	
	/**
	 * Finds the card that is painted at the given location. 
	 * Currently the algorithm is just to brute force search over all stacks on the table.
	 * May need to improve this algorithm for better performance.
	 */
	public Card getCardAt(int x, int y) {
		ArrayList<CardStack> stacks = model.getCardStacks();
		for(int i=stacks.size()-1; i>=0; i--) { // go backwards to hit top stacks first
			CardStack stack = stacks.get(i);
			if(x < stack.getX() - ImageLoader.IMAGE_WIDTH/2 ||
					x > stack.getX() + stack.getCards().size() + ImageLoader.IMAGE_WIDTH/2 ||
					y < stack.getY() - ImageLoader.IMAGE_HEIGHT/2 ||
					y > stack.getY() + stack.getCards().size() + ImageLoader.IMAGE_HEIGHT/2)
				continue;
			int n = Math.min(x-stack.getX()+ImageLoader.IMAGE_WIDTH/2, y-stack.getY()+ImageLoader.IMAGE_HEIGHT/2);
			n = Math.min(n, stack.size()-1);
			if(n<0) System.err.println("ERROR! n<0 in getCardAt() in TableComponent!");
			if(x<stack.getX()+ImageLoader.IMAGE_WIDTH/2+n && y<stack.getY()+ImageLoader.IMAGE_HEIGHT/2+n)
				return stack.getCards().get(n);
		}
		return null;
	}
	
	/**
	 * Finds the card that is painted at the given location. 
	 * Currently the algorithm is just to brute force search over all stacks on the table.
	 * May need to improve this algorithm for better performance.
	 */
	public StackFragment getSelectionAt(int x, int y) {
		return getSelectionAt(x, y, null);
	}
	
	public StackFragment getSelectionAt(int x, int y, TableSelection ignore) {
		ArrayList<CardStack> stacks = model.getCardStacks();
		for(int i=stacks.size()-1; i>=0; i--) { // go backwards to hit top stacks first
			CardStack stack = stacks.get(i);
			if(ignore!=null && ignore.containsFragment(stack))
				continue;
			if(x < stack.getX() - ImageLoader.IMAGE_WIDTH/2 ||
					x > stack.getX() + stack.getCards().size() + ImageLoader.IMAGE_WIDTH/2 ||
					y < stack.getY() - ImageLoader.IMAGE_HEIGHT/2 ||
					y > stack.getY() + stack.getCards().size() + ImageLoader.IMAGE_HEIGHT/2)
				continue;
			int dx = (stack.getX()+stack.size()-x);
			int dy = (stack.getY()+stack.size()-y);
			if(dx*dx+dy*dy<AFFORDANCE_BUBBLE_RADIUS*AFFORDANCE_BUBBLE_RADIUS)
				return new StackFragment(stack, stack.size());
			int n = Math.min(x-stack.getX()+ImageLoader.IMAGE_WIDTH/2, y-stack.getY()+ImageLoader.IMAGE_HEIGHT/2);
			n = Math.min(n, stack.size()-1);
			if(x<stack.getX()+ImageLoader.IMAGE_WIDTH/2+n && y<stack.getY()+ImageLoader.IMAGE_HEIGHT/2+n)
				return new StackFragment(stack, stack.size()-n);
		}
		return null;
	}
	

	public void paint(Graphics tg) {
		Graphics2D g = (Graphics2D)tg;
		
		Point tp = MouseInfo.getPointerInfo().getLocation();
		/** The position of the mouse cursor within the table component. */
		Point currentMouseLocation = new Point(tp.x-getLocationOnScreen().x, tp.y-getLocationOnScreen().y);
		StackFragment currentMouseSelection = getSelectionAt(currentMouseLocation.x, currentMouseLocation.y);
		StackFragment dragTarget = (!dragging)?currentMouseSelection:
			getSelectionAt(currentMouseLocation.x, currentMouseLocation.y, selection);
		//Color the background dark green
		g.setColor(new Color(0, 100, 0));
		g.fillRect(0, 0, getWidth(), getHeight());
		
		//Draw the card stacks on the table
		for(CardStack stack: model.getCardStacks()) {
			int depth = stack.size();
			if(dragging && selection.containsFragment(stack))
				depth-=selection.getFragment(stack).top;
			drawCardStack(g, stack, depth, selection);
			
			int frontCardCenterX = stack.getX()+stack.size();
			int frontCardCenterY = stack.getY()+stack.size();
			
			if((dragging && !selection.containsFragment(stack)) || MetaDeckController.getInstance().getGame().isHandDragging() ) {
				//Draw drag helper affordances
				
				g.setColor(Color.GREEN);
				if(dragTarget!=null && dragTarget.stack==stack) {
					
					g.drawRect(frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2, frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2, 
							ImageLoader.IMAGE_WIDTH+2, ImageLoader.IMAGE_HEIGHT+2);
					g.fillOval(frontCardCenterX-AFFORDANCE_BUBBLE_RADIUS, frontCardCenterY-AFFORDANCE_BUBBLE_RADIUS, 
							AFFORDANCE_BUBBLE_RADIUS*2, AFFORDANCE_BUBBLE_RADIUS*2);
				} else {
					g.drawOval(frontCardCenterX-AFFORDANCE_BUBBLE_RADIUS, frontCardCenterY-AFFORDANCE_BUBBLE_RADIUS, 
							AFFORDANCE_BUBBLE_RADIUS*2, AFFORDANCE_BUBBLE_RADIUS*2);
					g.drawOval(frontCardCenterX-AFFORDANCE_BUBBLE_RADIUS-1, frontCardCenterY-AFFORDANCE_BUBBLE_RADIUS-1, 
							AFFORDANCE_BUBBLE_RADIUS*2+2, AFFORDANCE_BUBBLE_RADIUS*2+2);
				}
			}
			if(!dragging && !MetaDeckController.getInstance().getGame().isHandDragging()
					&& selectionBoxOrigin==null && currentMouseSelection!=null && currentMouseSelection.stack==stack ) { 
				// Draw card selection affordances
				g.setColor(Color.GREEN);
				if(selection==null || !selection.containsFragment(stack) ||selection.getFragment(stack).top!=stack.size()) {
					if(currentMouseSelection.top==stack.size()) {				
						g.fillOval(frontCardCenterX-AFFORDANCE_BUBBLE_RADIUS, frontCardCenterY-AFFORDANCE_BUBBLE_RADIUS, 
								AFFORDANCE_BUBBLE_RADIUS*2, AFFORDANCE_BUBBLE_RADIUS*2);
					} else {
						g.drawOval(frontCardCenterX-AFFORDANCE_BUBBLE_RADIUS, frontCardCenterY-AFFORDANCE_BUBBLE_RADIUS, 
								AFFORDANCE_BUBBLE_RADIUS*2, AFFORDANCE_BUBBLE_RADIUS*2);
						g.drawOval(frontCardCenterX-AFFORDANCE_BUBBLE_RADIUS-1, frontCardCenterY-AFFORDANCE_BUBBLE_RADIUS-1, 
								AFFORDANCE_BUBBLE_RADIUS*2+2, AFFORDANCE_BUBBLE_RADIUS*2+2);
					}
				}
				g.drawRect(frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2, frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2, 
						ImageLoader.IMAGE_WIDTH+2, ImageLoader.IMAGE_HEIGHT+2);
				int selectAffordanceDepth = currentMouseSelection.top;
				if(selection!=null && selection.containsFragment(currentMouseSelection.stack) && currentMouseSelection.top==1)
					selectAffordanceDepth = selection.getFragment(currentMouseSelection.stack).top;
				if(selectAffordanceDepth>1) {
					g.drawLine(frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2, frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2, 
							frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2-(selectAffordanceDepth-1), 
							frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2-(selectAffordanceDepth-1));
					g.drawLine(frontCardCenterX+ImageLoader.IMAGE_WIDTH/2+1, frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2, 
							frontCardCenterX+ImageLoader.IMAGE_WIDTH/2-(selectAffordanceDepth-1)+1, 
							frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2-(selectAffordanceDepth-1));
					g.drawLine(frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2, frontCardCenterY+ImageLoader.IMAGE_HEIGHT/2, 
							frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2-(selectAffordanceDepth-1), 
							frontCardCenterY+ImageLoader.IMAGE_HEIGHT/2-(selectAffordanceDepth-1));
					g.drawLine(frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2-(selectAffordanceDepth-1), 
							frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2-(selectAffordanceDepth-1),
							frontCardCenterX+ImageLoader.IMAGE_WIDTH/2-(selectAffordanceDepth-1)+1, 
							frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2-(selectAffordanceDepth-1));
					g.drawLine(frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2-(selectAffordanceDepth-1), 
							frontCardCenterY-ImageLoader.IMAGE_HEIGHT/2-2-(selectAffordanceDepth-1),
							frontCardCenterX-ImageLoader.IMAGE_WIDTH/2-2-(selectAffordanceDepth-1), 
							frontCardCenterY+ImageLoader.IMAGE_HEIGHT/2-(selectAffordanceDepth-1));
					g.drawString(""+selectAffordanceDepth, 
							frontCardCenterX-selectAffordanceDepth+7, 
							frontCardCenterY-selectAffordanceDepth-ImageLoader.IMAGE_HEIGHT/2+2);
				}
				
			}
			//Draw stack size TODO make prettier
			g.setColor(Color.CYAN);
			if(depth>1)
				g.drawString(depth+"", stack.getX()-13, stack.getY()-ImageLoader.IMAGE_HEIGHT/2+2);
		}
		
		//Draw the selection box if necessary
		if(selectionBoxOrigin!=null) {
			g.setColor(Color.RED);
			int ax = Math.min(selectionBoxOrigin.x, currentMouseLocation.x);
			int ay = Math.min(selectionBoxOrigin.y, currentMouseLocation.y);
			int bx = Math.max(selectionBoxOrigin.x, currentMouseLocation.x);
			int by = Math.max(selectionBoxOrigin.y, currentMouseLocation.y);
			g.drawRect(ax, ay, bx-ax, by-ay);
		}
		
	}
	
	public static void drawCardStack(Graphics2D g, CardStack stack, int drawDepth, TableSelection selection) {
		int xpos = stack.getX();
		int ypos = stack.getY();
		
		g.setColor(Color.BLACK);
		for(int n=0; n<drawDepth; n++) {
			if(selection!=null && selection.containsFragment(stack) && n==stack.size()-selection.getFragment(stack).top)
				g.setColor(Color.RED);
			g.drawRect(xpos-ImageLoader.IMAGE_WIDTH/2-2, ypos-ImageLoader.IMAGE_HEIGHT/2-2, 
					ImageLoader.IMAGE_WIDTH+3, ImageLoader.IMAGE_HEIGHT+3);
			if(n==drawDepth-1) {
				Image im = stack.getFaceUp()?ImageLoader.loadImage(stack.getCards().get(n)):ImageLoader.loadImage("back");
				g.drawImage(im, 
					xpos-ImageLoader.IMAGE_WIDTH/2, ypos-ImageLoader.IMAGE_HEIGHT/2, null);
			}
			xpos+=1;
			ypos+=1;
		}
	}
}